#!/bin/sh -efu
#
# Copyright (C) 2018  Alexey Gladkov <legion@altlinux.org>
# Copyright (C) 2016  Dmitry V. Levin <ldv@altlinux.org>
# Copyright (C) 2008  Alexey I. Froloff <raorn@altlinux.org>
# Copyright (C) 2021  Arseny Maslennikov <arseny@altlinux.org>
#
# gear-changelog generate changelog entry from git commit logs.
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#

. gear-utils-sh-functions

rules="${RULES-}"
main_tree_id='HEAD'
changelog_rules='.gear/changelog'
cwidth=70
format='rpm'
specfile=
tab="$(printf \\t)"

show_help() {
	cat <<EOF
Usage: $PROG [options] [<since>]

Options:

  --no-rules              do not use rules at all, changelog entries will be
                          written without aggregation (grouping, filtering, etc);
  --no-credits            do not print an author of changes;
  --no-groups             do not group entries as described in a rules file;
  --no-header             do not print the changelog header;
  --header-only           print only the changelog header;
  --no-spec               do not use information from specfile;
  -d, --since-date=DATE   show commits more recent than a specific date;
  -f, --format=TYPE       specify source type: 'deb', 'rpm' or 'gnu',
                          default is 'rpm';
  -r, --rules=RULES       name of a rules file, default is .gear/changelog;
  -V, --version           print program version and exit;
  -h, --help              show this text and exit.

Report bugs to http://bugzilla.altlinux.org/

EOF
	exit
}

print_version() {
	cat <<EOF
$PROG version $PROG_VERSION
Written by Alexey Gladkov <legion@altlinux.org>

Copyright (C) 2018  Alexey Gladkov <legion@altlinux.org>
Copyright (C) 2016  Dmitry V. Levin <ldv@altlinux.org>
Copyright (C) 2008  Alexey I. Froloff <raorn@altlinux.org>
Copyright (C) 2021  Arseny Maslennikov <arseny@altlinux.org>
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
EOF
	exit
}

find_changelog_rules() {
	: >"$workdir/rules"
	if [ -n "$(git ls-tree "$main_tree_id" "$changelog_rules")" ]; then
		cat_blob "$main_tree_id" "$changelog_rules" >"$workdir/rules"
	fi
	rules_cleanup "$workdir/rules"
	changelog_rules="$workdir/rules"
}

num_groups=0
write_group() {
	[ -n "$regexp" ] ||
		fatal 'Group with empty regexp found.'

	mkdir -p -- "$workdir/$num_groups"
	printf '%s\n' "$group"  >"$workdir/$num_groups/descr"
	printf '%s\n' "$regexp" >"$workdir/$num_groups/regexp"
	printf '%s\n' "$filter" >"$workdir/$num_groups/filter"

	num_groups=$(($num_groups+1))
	filter='' regexp=''
}

parse_changelog_rules() {
	local found='' group='' regexp='' filter=''
	local cmd value

	while read -r cmd value; do
		case "$cmd" in
			''|'#'*) continue
				;;
			width:)
				[ -z "$group" ] ||
					fatal "Directive \`$cmd' cannot be specified inside group."

				cwidth="$(opt_check_number width "$value")"
				;;
			group:)
				[ -n "$value" ] ||
					fatal "Directive \`$cmd' cannot be specified without a value."

				[ -z "$group" ] || [ "$group" = "$value" ] ||
					write_group "$num_groups"

				group="$value"
				found=1
				;;
			regexp:) regexp="${regexp:+$regexp
}$value"
				;;
			filter:) filter="${filter:+$filter
}$value"
				;;
		esac
	done < "$changelog_rules"

	[ -z "$found" ] ||
		write_group
}

TEMP=`getopt -n $PROG -o 'd:f:,r:,h,V' -l 'format:,no-credits,no-groups,no-header,header-only,no-rules,no-spec,rules:,since-date:,help,version' -- "$@"` ||
	show_usage
eval set -- "$TEMP"

no_rules=
no_credits=
no_groups=
no_header=
header_only=
no_specfile=
since_date=
while :; do
	case "$1" in
		--no-credits) no_credits=1
			;;
		--no-groups) no_groups=1
			;;
		--no-header) no_header=1
			;;
		--header-only)
			header_only=1
			no_rules=1
			;;
		--no-rules) no_rules=1
			;;
		--no-spec) no_specfile=1
			;;
		-r|--rules) shift; changelog_rules="$1"
			;;
		-d|--since-date) shift; since_date="$1"
			;;
		-f|--format) shift
			[ -z "$1" ] &&
				fatal 'Empty format does not make sense!' ||
				format="$1"
			;;
		-h|--help) show_help
			;;
		-V|--version) print_version
			;;
		--) shift; break
			;;
	esac
	shift
done
case "$no_header:$header_only" in
	1:1) fatal '--no-header and --header-only are mutually exclusive.' ;;
	*) ;;
esac

case "$format" in
	deb|rpm|gnu) ;;
	*) fatal "Unknown format: $format" ;;
esac

chdir_to_toplevel

[ "$#" -eq 0 ] ||
	since="$1"

[ -n "${since-}" ] ||
	since="$(git describe --abbrev=0)"

if [ -z "$no_specfile" ]; then
	guess_specfile

	[ -s "$specfile" ] ||
		fatal 'No specfile found.'

	get_NVR_from_spec "$specfile"
	pkg_name="$spec_name"
	pkg_epoch="$spec_epoch"
	pkg_version="$spec_version"
	pkg_release="$spec_release"
fi

install_cleanup_handler cleanup_workdir
workdir="$(mktemp -dt "$PROG.XXXXXXXXXX")"

if [ -z "$no_rules" ]; then
	if [ ! -s "$changelog_rules" ]; then
		find_changelog_rules

		[ -s "$changelog_rules" ] ||
			fatal 'No rules found.'
	fi
	parse_changelog_rules
fi

gear_config_option GIT_AUTHOR_NAME name "${GIT_AUTHOR_NAME-$(git config --get user.name)}"
gear_config_option GIT_AUTHOR_EMAIL email "${GIT_AUTHOR_EMAIL-$(git config --get user.email)}"

releaser="${GIT_AUTHOR_NAME-GIT_AUTHOR_NAME} <${GIT_AUTHOR_EMAIL-GIT_AUTHOR_EMAIL}>"

. "$0-$format"

if [ -n "$header_only" ]; then
	format_header
	format_footer
	exit
fi

git log \
	--no-merges \
	--pretty="format:%h$tab%at$tab%an$tab%ae$tab%s%n" \
	${since_date:+--since="$since_date"} \
	${since:+$since..HEAD} |
while IFS="$tab" read -r hash atime aname aemail subj; do
	[ -n "$hash" ] ||
		continue

	if [ -n "$no_rules" ]; then
		format_commit "$subj" >> "$workdir/group"
		continue
	fi

	i=0
	while [ "$num_groups" -gt "$i" ]; do
		if printf '%s' "$subj" |grep -E -qsf "$workdir/$i/regexp"; then
			[ ! -s "$workdir/$i/filter" ] ||
				subj="$(printf '%s\n' "$subj" |
					sed -r -f "$workdir/$i/filter")"

			[ -n "$subj" ] ||
				continue

			out="$workdir/group$i"
			[ -z "$no_groups" ] ||
				out="$workdir/group"

			format_commit "$subj" >> "$out"
			break
		fi
		i=$(($i+1))
	done
done

[ -n "$no_header" ] ||
	format_header
if [ -z "$no_rules$no_groups" ]; then
	i=0
	while [ "$num_groups" -gt "$i" ]; do
		if [ ! -s "$workdir/group$i" ]; then
			i=$(($i+1))
			continue
		fi
		group="$(cat "$workdir/$i/descr")"
		format_group "$group"
		uniq < "$workdir/group$i"
		i=$(($i+1))
	done
else
	[ ! -s "$workdir/group" ] ||
		uniq < "$workdir/group"
fi
format_footer
